package com.jzo2o.orders.manager.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.db.DbRuntimeException;
import cn.hutool.db.sql.Order;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.orders.dto.response.OrderResDTO;
import com.jzo2o.api.orders.dto.response.OrderSimpleResDTO;
import com.jzo2o.common.constants.UserType;
import com.jzo2o.common.enums.EnableStatusEnum;
import com.jzo2o.common.expcetions.CommonException;
import com.jzo2o.common.utils.BeanUtils;
import com.jzo2o.common.utils.CollUtils;
import com.jzo2o.common.utils.JsonUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.orders.base.config.OrderStateMachine;
import com.jzo2o.orders.base.enums.OrderPayStatusEnum;
import com.jzo2o.orders.base.enums.OrderRefundStatusEnum;
import com.jzo2o.orders.base.enums.OrderStatusEnum;
import com.jzo2o.orders.base.mapper.OrdersMapper;
import com.jzo2o.orders.base.model.domain.Orders;
import com.jzo2o.orders.base.model.domain.OrdersCanceled;
import com.jzo2o.orders.base.model.domain.OrdersRefund;
import com.jzo2o.orders.base.model.dto.OrderSnapshotDTO;
import com.jzo2o.orders.base.model.dto.OrderUpdateStatusDTO;
import com.jzo2o.orders.base.service.IOrdersCommonService;
import com.jzo2o.orders.manager.handler.OrdersHandler;
import com.jzo2o.orders.manager.model.dto.OrderCancelDTO;
import com.jzo2o.orders.manager.model.dto.response.OrdersPayResDTO;
import com.jzo2o.orders.manager.service.IOrdersCanceledService;
import com.jzo2o.orders.manager.service.IOrdersCreateService;
import com.jzo2o.orders.manager.service.IOrdersManagerService;
import com.jzo2o.orders.manager.service.IOrdersRefundService;
import com.jzo2o.redis.helper.CacheHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.jzo2o.orders.base.constants.FieldConstants.SORT_BY;
import static com.jzo2o.orders.base.constants.RedisConstants.RedisKey.ORDERS;

/**
 * <p>
 * 订单表 服务实现类
 * </p>
 *
 * @author itcast
 * @since 2023-07-10
 */
@Slf4j
@Service
public class OrdersManagerServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersManagerService {

    @Resource
    private OrdersManagerServiceImpl owner;

    @Resource
    private IOrdersCanceledService ordersCanceledService;

    @Resource
    private IOrdersCommonService ordersCommonService;

    @Resource
    private IOrdersCreateService ordersCreateService;

    @Resource
    private IOrdersRefundService ordersRefundService;

    @Resource
    private OrdersHandler ordersHandler;

    @Resource
    private OrderStateMachine orderStateMachine;

    @Resource
    private CacheHelper cacheHelper;

    @Override
    public List<Orders> batchQuery(List<Long> ids) {
        LambdaQueryWrapper<Orders> queryWrapper = Wrappers.<Orders>lambdaQuery().in(Orders::getId, ids).ge(Orders::getUserId, 0);
        return baseMapper.selectList(queryWrapper);
    }

    @Override
    public Orders queryById(Long id) {
        return baseMapper.selectById(id);
    }

    /**
     * 滚动分页查询
     *
     * @param currentUserId 当前用户id
     * @param ordersStatus  订单状态，0：待支付，100：派单中，200：待服务，300：服务中，400：待评价，500：订单完成，600：已取消，700：已关闭
     * @param sortBy        排序字段
     * @return 订单列表
     */
    @Override
    public List<OrderSimpleResDTO> consumerQueryList(Long currentUserId, Integer ordersStatus, Long sortBy) {
        //1.构件查询条件
        LambdaQueryWrapper<Orders> queryWrapper = Wrappers.<Orders>lambdaQuery()
                .eq(ObjectUtils.isNotNull(ordersStatus), Orders::getOrdersStatus, ordersStatus)
                .lt(ObjectUtils.isNotNull(sortBy), Orders::getSortBy, sortBy)
                .eq(Orders::getUserId, currentUserId)
                .eq(Orders::getDisplay, EnableStatusEnum.ENABLE.getStatus())
                .select(Orders::getId); //指定查询的字段是订单id
        Page<Orders> queryPage = new Page<>();
        queryPage.addOrder(OrderItem.desc(SORT_BY));
        queryPage.setSearchCount(false);

        //2.使用覆盖索引查询订单列表（只包括订单id）
        Page<Orders> ordersPage = baseMapper.selectPage(queryPage, queryWrapper);
        //将查询到的列表提取出订单id
        List<Orders> orderList = ordersPage.getRecords();
        //取出订单id
        List<Long> ids = CollUtils.getFieldValues(orderList, Orders::getId);
        //根据订单id查询聚集索引使用sql：select * from orders where id in(?,?,?,?)
//        List<Orders> ordersList = batchQuery(ids);

        String redisKey = String.format(ORDERS, currentUserId);        //将订单信息存入缓存
        //String dataType(redis key)
        //List<K> objectIds 订单id(多个)
        //BatchDataQueryExecutor<K,T> batchDataQueryExecutor 有一个Map<K,T> execute(List<K> objectIds(缓存中没有的id), Class<T> clazz 返回的数据类型)
        //Class<T> clazz 最终返回的数据类型
        //Long ttl 缓存过期时间
        List<OrderSimpleResDTO> orderSimpleResDTOS = cacheHelper.<Long, OrderSimpleResDTO>batchGet(redisKey, ids, (noCacheIds, clazz) -> {
            //查询数据库
            List<Orders> ordersList = batchQuery(noCacheIds);
            //将orderList转成map
            Map<Long, OrderSimpleResDTO> collect = ordersList.stream().collect(Collectors.toMap(Orders::getId, o -> BeanUtils.toBean(o, OrderSimpleResDTO.class)));
            return collect;

        }, OrderSimpleResDTO.class, 600L);

       // List<OrderSimpleResDTO> orderSimpleResDTOS = BeanUtil.copyToList(ordersList, OrderSimpleResDTO.class);
        return orderSimpleResDTOS;

    }
//    @Override
//    public List<OrderSimpleResDTO> consumerQueryList(Long currentUserId, Integer ordersStatus, Long sortBy) {
//        //1.构件查询条件
//        LambdaQueryWrapper<Orders> queryWrapper = Wrappers.<Orders>lambdaQuery()
//                .eq(ObjectUtils.isNotNull(ordersStatus), Orders::getOrdersStatus, ordersStatus)
//                .lt(ObjectUtils.isNotNull(sortBy), Orders::getSortBy, sortBy)
//                .eq(Orders::getUserId, currentUserId)
//                .eq(Orders::getDisplay, EnableStatusEnum.ENABLE.getStatus());
//        Page<Orders> queryPage = new Page<>();
//        queryPage.addOrder(OrderItem.desc(SORT_BY));
//        queryPage.setSearchCount(false);
//
//        //2.查询订单列表
//        Page<Orders> ordersPage = baseMapper.selectPage(queryPage, queryWrapper);
//        List<Orders> records = ordersPage.getRecords();
//        List<OrderSimpleResDTO> orderSimpleResDTOS = BeanUtil.copyToList(records, OrderSimpleResDTO.class);
//        return orderSimpleResDTOS;
//
//    }
    /**
     * 根据订单id查询
     *
     * @param id 订单id
     * @return 订单详情
     */
    @Override
    public OrderResDTO getDetail(Long id) {
//        Orders orders = queryById(id);
        //查询订单快照
        String currentSnapshot = orderStateMachine.getCurrentSnapshotCache(id.toString());
        //将json转成对象
        OrderSnapshotDTO orderSnapshotDTO = JsonUtils.toBean(currentSnapshot, OrderSnapshotDTO.class);

        //懒加载方式时取消支付超时的订单
        orderSnapshotDTO = cancelIfPayOverTime(orderSnapshotDTO);
        OrderResDTO orderResDTO = BeanUtil.toBean(orderSnapshotDTO, OrderResDTO.class);
        return orderResDTO;
    }

    /**
     * 懒加载法按时取消支付超时的订单
     * @param orderSnapshotDTO
     * @return
     */
    public OrderSnapshotDTO cancelIfPayOverTime(OrderSnapshotDTO orderSnapshotDTO){
        //判断订单是未支付且超时（凑够订单创i按时间按开始15分钟未支付）
        Integer ordersStatus = orderSnapshotDTO.getOrdersStatus();
        //判断订单是未支付且支付超时（从订单创建时间开始15分钟未支付）
        if(ordersStatus == OrderStatusEnum.NO_PAY.getStatus() &&orderSnapshotDTO.getCreateTime().isBefore(LocalDateTime.now().minusMinutes(15)) ){
            //查询一下最新的支付状态，如果是未支付，再执行下边的取消代码
            OrdersPayResDTO payResultFromTradServer = ordersCreateService.getPayResultFromTradServer(orderSnapshotDTO.getId());
            //如果没有支付成功，
            if(ObjectUtils.isNotNull(payResultFromTradServer) && payResultFromTradServer.getPayStatus() != OrderPayStatusEnum.PAY_SUCCESS.getStatus()){

                OrderCancelDTO orderCancelDTO = BeanUtils.toBean(orderSnapshotDTO, OrderCancelDTO.class);
                orderCancelDTO.setCurrentUserType(UserType.SYSTEM);
                orderCancelDTO.setCancelReason("订单支付超时系统自动取消");
                cancelByNoPay(orderCancelDTO);
                //查询最新的订单信息
                //orders = getById(orders.getId());

                //上边执行cancelByNoPay方法因为使用了状态机，取消订单后存入新的快照，这里查询新的快照
                String currentSnapshot = orderStateMachine.getCurrentSnapshot(orderSnapshotDTO.getId().toString());
                orderSnapshotDTO = JsonUtils.toBean(currentSnapshot, OrderSnapshotDTO.class);
                return orderSnapshotDTO;
            }

        }
        return orderSnapshotDTO;
    }
//    public Orders cancelIfPayOverTime(Orders orders){
//        //判断订单是未支付且超时（凑够订单创i按时间按开始15分钟未支付）
//        Integer ordersStatus = orders.getOrdersStatus();
//        //判断订单是未支付且支付超时（从订单创建时间开始15分钟未支付）
//        if(ordersStatus == OrderStatusEnum.NO_PAY.getStatus() &&orders.getCreateTime().isBefore(LocalDateTime.now().minusMinutes(15)) ){
//            //查询一下最新的支付状态，如果是未支付，再执行下边的取消代码
//            OrdersPayResDTO payResultFromTradServer = ordersCreateService.getPayResultFromTradServer(orders.getId());
//            //如果没有支付成功，
//            if(ObjectUtils.isNotNull(payResultFromTradServer) && payResultFromTradServer.getPayStatus() != OrderPayStatusEnum.PAY_SUCCESS.getStatus()){
//
//                OrderCancelDTO orderCancelDTO = BeanUtils.toBean(orders, OrderCancelDTO.class);
//                orderCancelDTO.setCurrentUserType(UserType.SYSTEM);
//                orderCancelDTO.setCancelReason("订单支付超时系统自动取消");
//                cancelByNoPay(orderCancelDTO);
//                //查询最新的订单信息
//                orders = getById(orders.getId());
//                return orders;
//            }
//
//        }
//        return orders;
//    }

    /**
     * 订单评价
     *
     * @param ordersId 订单id
     */
    @Override
    @Transactional
    public void evaluationOrder(Long ordersId) {
//        //查询订单详情
//        Orders orders = queryById(ordersId);
//
//        //构建订单快照
//        OrderSnapshotDTO orderSnapshotDTO = OrderSnapshotDTO.builder()
//                .evaluationTime(LocalDateTime.now())
//                .build();
//
//        //订单状态变更
//        orderStateMachine.changeStatus(orders.getUserId(), orders.getId().toString(), OrderStatusChangeEventEnum.EVALUATE, orderSnapshotDTO);
    }

    @Override
    public void cancel(OrderCancelDTO orderCancelDTO) {
        Long orderId = orderCancelDTO.getId();

        //判断该订单是否存在
        Orders orders = getById(orderId);
        if(ObjectUtils.isNull(orders)){
            throw new CommonException("要取消的订单不存在");
        }
        //将订单中的交易单号信息拷贝到orderCancelDTO
        orderCancelDTO.setTradingOrderNo(orders.getTradingOrderNo());
        orderCancelDTO.setRealPayAmount(orders.getRealPayAmount());

        //取出订单状态
        Integer ordersStatus = orders.getOrdersStatus();

        //对未支付订单的取消操作
        if(ordersStatus == OrderStatusEnum.NO_PAY.getStatus()){
            owner.cancelByNoPay(orderCancelDTO);
        }else if(ordersStatus == OrderStatusEnum.DISPATCHING.getStatus()){
            //对已支付订单的操作。
            owner.cancelByDispatching(orderCancelDTO);
            //新启一个线程请求支付服务进行退款
            ordersHandler.requestRefundNewThread(orderCancelDTO.getId());
        }else{
            throw new CommonException("");
        }


    }

    /**
     * 取消未支付的订单
     * @param orderCancelDTO
     */
    @Transactional(rollbackFor = Exception.class)
    public void cancelByNoPay(OrderCancelDTO orderCancelDTO) {
        //添加订单取消记录
        OrdersCanceled ordersCanceled = BeanUtils.toBean(orderCancelDTO, OrdersCanceled.class);
        //取消人id
        ordersCanceled.setCancellerId(orderCancelDTO.getCurrentUserId());
        //取消人名称
        ordersCanceled.setCancelerName(orderCancelDTO.getCurrentUserName());
        //取消人的类型
        ordersCanceled.setCancellerType(orderCancelDTO.getCurrentUserType());
        //取消时间
        ordersCanceled.setCancelTime(LocalDateTime.now());
        ordersCanceledService.save(ordersCanceled);

        //修改订单状态为已取消
        OrderUpdateStatusDTO orderUpdateStatusDTO = new OrderUpdateStatusDTO();
        //填充订单id
        orderUpdateStatusDTO.setId(orderCancelDTO.getId());
        //将未支付的状态最终改为已取消
        //原始状态-未支付
        orderUpdateStatusDTO.setOriginStatus(OrderStatusEnum.NO_PAY.getStatus());
        //目标状态-已取消
        orderUpdateStatusDTO.setTargetStatus(OrderStatusEnum.CANCELED.getStatus());
        //最终要执行的sql update orders set orders_status=600 where id = ? and orders_status = 0
        Integer integer = ordersCommonService.updateStatus(orderUpdateStatusDTO);
        if(integer <= 0){
            throw new DbRuntimeException("订单取消事件处理失败");
        }
    }
    @Transactional(rollbackFor = Exception.class)
    public void cancelByDispatching(OrderCancelDTO orderCancelDTO){
        //添加订单取消记录
        OrdersCanceled ordersCanceled = BeanUtils.toBean(orderCancelDTO, OrdersCanceled.class);
        //取消人id
        ordersCanceled.setCancellerId(orderCancelDTO.getCurrentUserId());
        //取消人名称
        ordersCanceled.setCancelerName(orderCancelDTO.getCurrentUserName());
        //取消人的类型
        ordersCanceled.setCancellerType(orderCancelDTO.getCurrentUserType());
        //取消时间
        ordersCanceled.setCancelTime(LocalDateTime.now());
        ordersCanceledService.save(ordersCanceled);

        //修改订单状态为已取消
        OrderUpdateStatusDTO orderUpdateStatusDTO = new OrderUpdateStatusDTO();
        //填充订单id
        orderUpdateStatusDTO.setId(orderCancelDTO.getId());
        //将未支付的状态最终改为已取消
        //原始状态-派单中
        orderUpdateStatusDTO.setOriginStatus(OrderStatusEnum.DISPATCHING.getStatus());
        //目标状态-已关闭
        orderUpdateStatusDTO.setTargetStatus(OrderStatusEnum.CLOSED.getStatus());
        //设置退款状态为退款中
        orderUpdateStatusDTO.setRefundStatus(OrderRefundStatusEnum.REFUNDING.getStatus());
        //最终要执行的sql update orders set orders_status=600 where id = ? and orders_status = 0
        Integer integer = ordersCommonService.updateStatus(orderUpdateStatusDTO);
        if(integer <= 0){
            throw new DbRuntimeException("订单取消事件处理失败");
        }

        //保存退款记录
        OrdersRefund ordersRefund = BeanUtils.toBean(orderCancelDTO, OrdersRefund.class);

        ordersRefundService.save(ordersRefund);
    }
}
